// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/audio/null_audio_sink.h"

#include "base/bind.h"
#include "base/location.h"
#include "base/single_thread_task_runner.h"
#include "media/audio/fake_audio_worker.h"
#include "media/base/audio_hash.h"

namespace media {

NullAudioSink::NullAudioSink(
    const scoped_refptr<base::SingleThreadTaskRunner>& task_runner)
    : initialized_(false)
    , started_(false)
    , playing_(false)
    , callback_(NULL)
    , task_runner_(task_runner)
{
}

NullAudioSink::~NullAudioSink() { }

void NullAudioSink::Initialize(const AudioParameters& params,
    RenderCallback* callback)
{
    DCHECK(!started_);
    fake_worker_.reset(new FakeAudioWorker(task_runner_, params));
    audio_bus_ = AudioBus::Create(params);
    callback_ = callback;
    initialized_ = true;
}

void NullAudioSink::Start()
{
    DCHECK(task_runner_->BelongsToCurrentThread());
    DCHECK(initialized_);
    DCHECK(!started_);
    started_ = true;
}

void NullAudioSink::Stop()
{
    DCHECK(task_runner_->BelongsToCurrentThread());
    started_ = false;
    // Stop may be called at any time, so we have to check before stopping.
    if (fake_worker_)
        fake_worker_->Stop();
}

void NullAudioSink::Play()
{
    DCHECK(task_runner_->BelongsToCurrentThread());
    DCHECK(started_);

    if (playing_)
        return;

    fake_worker_->Start(base::Bind(
        &NullAudioSink::CallRender, base::Unretained(this)));

    playing_ = true;
}

void NullAudioSink::Pause()
{
    DCHECK(task_runner_->BelongsToCurrentThread());
    DCHECK(started_);

    if (!playing_)
        return;

    fake_worker_->Stop();
    playing_ = false;
}

bool NullAudioSink::SetVolume(double volume)
{
    // Audio is always muted.
    return volume == 0.0;
}

OutputDeviceInfo NullAudioSink::GetOutputDeviceInfo()
{
    return OutputDeviceInfo(OUTPUT_DEVICE_STATUS_OK);
}

bool NullAudioSink::CurrentThreadIsRenderingThread()
{
    return task_runner_->BelongsToCurrentThread();
}

void NullAudioSink::SwitchOutputDevice(const std::string& device_id,
    const url::Origin& security_origin,
    const OutputDeviceStatusCB& callback)
{
    callback.Run(OUTPUT_DEVICE_STATUS_ERROR_INTERNAL);
}

void NullAudioSink::CallRender()
{
    DCHECK(task_runner_->BelongsToCurrentThread());

    int frames_received = callback_->Render(
        base::TimeDelta(), base::TimeTicks::Now(), 0, audio_bus_.get());
    if (!audio_hash_ || frames_received <= 0)
        return;

    audio_hash_->Update(audio_bus_.get(), frames_received);
}

void NullAudioSink::StartAudioHashForTesting()
{
    DCHECK(!initialized_);
    audio_hash_.reset(new AudioHash());
}

std::string NullAudioSink::GetAudioHashForTesting()
{
    return audio_hash_ ? audio_hash_->ToString() : std::string();
}

} // namespace media
